sysroot: Add a try_lock() API
authorColin Walters <walters@verbum.org>
Thu, 7 May 2015 02:19:05 +0000 (22:19 -0400)
committerColin Walters <walters@verbum.org>
Sun, 10 May 2015 20:20:53 +0000 (16:20 -0400)
The blocking locking API wasn't sufficient for use in the rpm-ostree
daemon; it really wants to know if the lock is held, then continue to
do other things (like service DBus requests), and get notification
when the lock is available.

We also add an async variant that can be called if the lock is not
available.

Implement a higher level "loop until lock is available" method in the
`ostree admin` commandline.

doc/ostree-sections.txt
libglnx
src/libostree/ostree-sysroot.c
src/libostree/ostree-sysroot.h
src/ostree/ot-admin-builtin-deploy.c
src/ostree/ot-admin-functions.c
src/ostree/ot-admin-functions.h

index b1b86f01d9d887a2b0a9adf5229b817c60b6b293..2cfc8fa6f490a468616a2516c95e5b28c05664c1 100644 (file)
@@ -368,6 +368,9 @@ ostree_sysroot_new_default
 ostree_sysroot_get_path
 ostree_sysroot_load
 ostree_sysroot_lock
+ostree_sysroot_try_lock
+ostree_sysroot_lock_async
+ostree_sysroot_lock_finish
 ostree_sysroot_unlock
 ostree_sysroot_get_fd
 ostree_sysroot_ensure_initialized
diff --git a/libglnx b/libglnx
index cf8ae27bab717621f1edf8ab6ae3dd89da4d8d4f..900b25f7018878ab64bd04751d8f15c6d83ba823 160000 (submodule)
--- a/libglnx
+++ b/libglnx
@@ -1 +1 @@
-Subproject commit cf8ae27bab717621f1edf8ab6ae3dd89da4d8d4f
+Subproject commit 900b25f7018878ab64bd04751d8f15c6d83ba823
index bdd67917f05ced92c6e693b2954cac75b1e42e32..ebcb63296984a59ab793998a28007560938ccaf9 100644 (file)
@@ -1170,6 +1170,55 @@ ostree_sysroot_lock (OstreeSysroot     *self,
                               LOCK_EX, &self->lock, error);
 }
 
+/**
+ * ostree_sysroot_try_lock:
+ * @self: Self
+ * @out_acquired: (out): Whether or not the lock has been acquired
+ * @error: Error
+ *
+ * Try to acquire an exclusive multi-process write lock for @self.  If
+ * another process holds the lock, this function will return
+ * immediately, setting @out_acquired to %FALSE, and returning %TRUE
+ * (and no error).
+ *
+ * Release the lock with ostree_sysroot_unlock().  The lock will also
+ * be released if @self is deallocated.
+ */
+gboolean
+ostree_sysroot_try_lock (OstreeSysroot         *self,
+                         gboolean              *out_acquired,
+                         GError               **error)
+{
+  gboolean ret = FALSE;
+  GError *local_error = NULL;
+
+  if (!ensure_sysroot_fd (self, error))
+    goto out;
+
+  /* Note use of LOCK_NB */
+  if (!glnx_make_lock_file (self->sysroot_fd, OSTREE_SYSROOT_LOCKFILE,
+                            LOCK_EX | LOCK_NB, &self->lock, &local_error))
+    {
+      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        {
+          *out_acquired = FALSE;
+        }
+      else
+        {
+          g_propagate_error (error, local_error);
+          goto out;
+        }
+    }
+  else
+    {
+      *out_acquired = TRUE;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
 /**
  * ostree_sysroot_unlock:
  * @self: Self
@@ -1184,6 +1233,64 @@ ostree_sysroot_unlock (OstreeSysroot  *self)
   glnx_release_lock_file (&self->lock);
 }
 
+static void
+lock_in_thread (GTask            *task,
+                gpointer          source,
+                gpointer          task_data,
+                GCancellable     *cancellable)
+{
+  GError *local_error = NULL;
+  OstreeSysroot *self = source;
+
+  if (!ostree_sysroot_lock (self, &local_error))
+    goto out;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, &local_error))
+    ostree_sysroot_unlock (self);
+  
+ out:
+  if (local_error)
+    g_task_return_error (task, local_error);
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * ostree_sysroot_lock_async:
+ * @self: Self
+ * @cancellable: Cancellable
+ * @callback: Callback
+ * @user_data: User data
+ * 
+ * An asynchronous version of ostree_sysroot_lock().
+ */
+void
+ostree_sysroot_lock_async (OstreeSysroot         *self,
+                           GCancellable          *cancellable,
+                           GAsyncReadyCallback    callback,
+                           gpointer               user_data)
+{
+  g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
+  g_task_run_in_thread (task, lock_in_thread);
+}
+
+/**
+ * ostree_sysroot_lock_finish:
+ * @self: Self
+ * @result: Result
+ * @error: Error
+ * 
+ * Call when ostree_sysroot_lock_async() is ready.
+ */
+gboolean
+ostree_sysroot_lock_finish (OstreeSysroot         *self,
+                            GAsyncResult          *result,
+                            GError               **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+  return g_task_propagate_boolean ((GTask*)result, error);
+}
+
 /**
  * ostree_sysroot_simple_write_deployment:
  * @sysroot: Sysroot
index e7f6e482984b68e992991852708b13ba4445e1d3..ce128bbafeea126c78091558fb9b95ad5b048f7b 100644 (file)
@@ -63,6 +63,16 @@ char *ostree_sysroot_get_deployment_dirpath (OstreeSysroot    *self,
 GFile * ostree_sysroot_get_deployment_origin_path (GFile   *deployment_path);
 
 gboolean ostree_sysroot_lock (OstreeSysroot  *self, GError **error);
+gboolean ostree_sysroot_try_lock (OstreeSysroot         *self,
+                                  gboolean              *out_acquired,
+                                  GError               **error);
+void     ostree_sysroot_lock_async (OstreeSysroot         *self,
+                                    GCancellable          *cancellable,
+                                    GAsyncReadyCallback    callback,
+                                    gpointer               user_data);
+gboolean ostree_sysroot_lock_finish (OstreeSysroot         *self,
+                                     GAsyncResult          *result,
+                                     GError               **error);
 void ostree_sysroot_unlock (OstreeSysroot  *self);
 
 gboolean ostree_sysroot_cleanup (OstreeSysroot       *self,
index b18b3fd3e50321385f804df212c9904ff501ef2f..78d60bb5c7545e45f8a6bc7b8622ff5a3e6f5cbc 100644 (file)
@@ -79,7 +79,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
 
   refspec = argv[1];
 
-  if (!ostree_sysroot_lock (sysroot, error))
+  if (!ot_admin_sysroot_lock (sysroot, error))
     goto out;
 
   if (!ostree_sysroot_load (sysroot, cancellable, error))
index a29d15587a56b67454b0af6181c557c73c93bd16..c818a00c6b182c35978f4834f1269ae56705f633 100644 (file)
@@ -96,3 +96,62 @@ ot_admin_get_indexed_deployment (OstreeSysroot  *sysroot,
   
   return g_object_ref (current_deployments->pdata[index]);
 }
+
+struct ContextState {
+  GMainContext *mainctx;
+  gboolean running;
+};
+
+static gboolean
+on_sysroot_lock_timeout (gpointer user_data)
+{
+  g_print ("Waiting for sysroot lock...\n");
+  return TRUE;
+}
+
+static void
+on_sysroot_lock_acquired (OstreeSysroot       *sysroot,
+                          GAsyncResult        *result,
+                          struct ContextState *state)
+{
+  state->running = FALSE;
+  g_main_context_wakeup (state->mainctx);
+}
+
+gboolean
+ot_admin_sysroot_lock (OstreeSysroot  *sysroot,
+                       GError        **error)
+{
+  gboolean ret = FALSE;
+  gboolean acquired;
+  struct ContextState state = {
+    .mainctx = g_main_context_new (),
+    .running = TRUE,
+  };
+
+  g_main_context_push_thread_default (state.mainctx);
+
+  if (!ostree_sysroot_try_lock (sysroot, &acquired, error))
+    goto out;
+
+  if (!acquired)
+    {
+      GSource *timeout_src = g_timeout_source_new_seconds (3);
+      g_source_set_callback (timeout_src, (GSourceFunc)on_sysroot_lock_timeout, &state, NULL);
+      g_source_attach (timeout_src, state.mainctx);
+      g_source_unref (timeout_src);
+      
+      on_sysroot_lock_timeout (&state);
+
+      ostree_sysroot_lock_async (sysroot, NULL, (GAsyncReadyCallback)on_sysroot_lock_acquired, &state);
+
+      while (state.running)
+        g_main_context_iteration (state.mainctx, TRUE);
+    }
+
+  ret = TRUE;
+ out:
+  g_main_context_pop_thread_default (state.mainctx);
+  g_main_context_unref (state.mainctx);
+  return ret;
+}
index 49b7039d5d5207ed063490393c111471ff4e3c16..67164ca5f83b181fed139735d3341f798dad1089 100644 (file)
@@ -41,5 +41,9 @@ ot_admin_get_indexed_deployment (OstreeSysroot  *sysroot,
                                  int             index,
                                  GError        **error);
 
+gboolean
+ot_admin_sysroot_lock (OstreeSysroot  *sysroot,
+                       GError        **error);
+
 
 G_END_DECLS